package mspbots.next.ticket.core.grader.interceptor;

import com.fasterxml.jackson.core.JsonProcessingException;
import com.fasterxml.jackson.core.type.TypeReference;
import com.fasterxml.jackson.databind.JsonNode;
import lombok.Data;
import lombok.RequiredArgsConstructor;
import lombok.extern.log4j.Log4j2;
import mspbots.next.ticket.core.grader.AbstractGradeRuleAdapter;
import mspbots.next.ticket.core.grader.GradeRuleInterceptorRegistration;
import mspbots.next.ticket.core.points.PointResult;
import org.springframework.stereotype.Service;
import org.springframework.util.CollectionUtils;
import org.springframework.util.ObjectUtils;
import org.springframework.util.StringUtils;
import reactor.core.publisher.Flux;
import reactor.core.publisher.Mono;

import java.util.ArrayList;
import java.util.Iterator;
import java.util.List;
import java.util.Map;
import java.util.concurrent.atomic.AtomicInteger;

@Log4j2
@Service(value = "MEMBER_SKILLS_POINT_RULE")
@RequiredArgsConstructor
public class MemberSkillsRuleInterceptor extends AbstractGradeRuleAdapter {

    private static final String RULE_KEY = "MemberSkills";
    private final List<String> userSkillList = new ArrayList<>();
    private List<MemberSkillsParam> params;
    private String identifier;

    @Override
    public Mono<GradeRuleInterceptorRegistration> prepare(GradeRuleInterceptorRegistration gradeRuleInterceptorRegistration) {
        var mono1 = this.r2dbcEntityTemplate.getDatabaseClient().sql("SELECT skills " +
                "FROM cw_user_skills_view WHERE tenant_code=:tenantCode and identifier=:identifier")
                .bind("tenantCode", gradeRuleInterceptorRegistration.getRule().getTenantId())
                .bind("identifier", gradeRuleInterceptorRegistration.getRule().getRequest().getIdentifier())
                .fetch().first()
                .doOnNext(re -> {
                    try {
                        JsonNode skillsNode = super.objectMapper.convertValue(objectMapper.readTree(re.get("skills").toString()), JsonNode.class);
                        if (!ObjectUtils.isEmpty(skillsNode) && !skillsNode.isEmpty()) {
                            Iterator<JsonNode> elements = skillsNode.elements();
                            while (elements.hasNext()) {
                                this.userSkillList.add(super.getStringValueName(elements.next().get("skill")));
                            }
                        }
                    } catch (JsonProcessingException e) {
                        e.printStackTrace();
                    }
                });

        return Flux.concatDelayError(mono1)
                .doFinally(r -> {
                    this.params = super.objectMapper
                            .convertValue(gradeRuleInterceptorRegistration.getRule().getParams(), new TypeReference<>() {
                            });
                    this.identifier = gradeRuleInterceptorRegistration.getRule().getRequest().getIdentifier();
                })
                .then(Mono.just(gradeRuleInterceptorRegistration));
    }

    @Override
    public Mono<PointResult> scoring(JsonNode ticket) {
        AtomicInteger order = new AtomicInteger();
        String type = super.getStringValueName(ticket.get("type"));
        String subType = super.getStringValueName(ticket.get("subType"));
        String ticketItem = super.getStringValueName(ticket.get("item"));

        if (!CollectionUtils.isEmpty(this.userSkillList) && StringUtils.hasLength(this.identifier) && type != null) {
            params.stream().filter(p -> p.isSelected() && !CollectionUtils.isEmpty(p.getMemberSkills()) && StringUtils.hasLength(p.getType()))
                    .forEach(item -> {
                                boolean flag = item.getMemberSkills().stream().anyMatch(this.userSkillList::contains);
                                if (flag && type.equals(item.getType())) {
                                    if (!StringUtils.hasLength(subType) && CollectionUtils.isEmpty(item.getSubtypes())) {
                                        order.addAndGet(item.getPoints());
                                    } else if (StringUtils.hasLength(subType) && !CollectionUtils.isEmpty(item.getSubtypes())
                                            && item.getSubtypes().contains(subType)) {
                                        if (!StringUtils.hasLength(ticketItem) && CollectionUtils.isEmpty(item.getItems())) {
                                            order.addAndGet(item.getPoints());
                                        } else if (StringUtils.hasLength(ticketItem) && !CollectionUtils.isEmpty(item.getItems())
                                                && item.getItems().contains(ticketItem)) {
                                            order.addAndGet(item.getPoints());
                                        }
                                    }
                                }
                            }
                    );
        }
        PointResult pointResult = PointResult.withDefault().point(order.get())
                .detail(PointResult.Detail.with(order.get(),
                        Map.of("type", type, "subType", subType, "item", ticketItem, "userSkills", userSkillList),
                        RULE_KEY));
        return Mono.just(pointResult);
    }

    @Data
    private static class MemberSkillsParam {
        private List<String> memberSkills;
        private String type;
        private List<String> subtypes;
        private List<String> items;
        private Integer points;
        private boolean selected;
    }

}
